package com.zlk.algorithm.dataStructure.random;

import org.junit.Test;

/**
 * @program: algorithm
 * @ClassName random  等概率问题
 * @description:1、 任意的x ,x属于[0,1), [0,x) 范围上的数出现的概率由原来的x调整成x 的平方
 *              2、给定函数 f  函数随机等概率返回 1到5在f函数基础上 返回一个等概率输出 1到7的函数g
 *              3、根据一个输出不等概率的0和1 函数f获得到一个 输出等概率函数的01 的函数g
 * @author: slfang
 * @create: 2023-12-12 09:46
 * @Version 1.0
 **/
public class RandomIndex {

    /**
     * Math.random()  -> double -> [0,1)  每次等概率返回一个小数值出来
     */

    @Test
    public void test1(){
        //[0,1) -> [0,8)
        double v = Math.random() * 8;

    }

    /**
     * 返回 [0,1)的一个小数
     * 任意的x ,x属于[0,1), [0,x) 范围上的数出现的概率由原来的x调整成x 的平方
     * @return
     */
    @Test
    public void test2(){
        double testTimes = 10000000;
        double x = 0.17;
        int count = 0;
        for (int i = 0; i < testTimes; i++) {
            if(xToPower2()<x){
                count++;
            }
        }

        int count2 = 0;
        for(int i = 0;i < testTimes;i++) {
            if(powReduceX() < x) {
                count2++;
            }
        }

        System.out.println((double) count/testTimes);

        System.out.println("1 - (1-[0," + x + "))^2)的概率是" + (double) count / (double) testTimes);
        System.out.println("1 - (1-[0," + x + "))^2)的概率是" + ((double)1 - Math.pow((1-x), 2)));
    }

    private double xToPower2() {
        // 因为取大值  所以两个都必须同时满足都在[0,x) 范围内不然超了
        return Math.max(Math.random(),Math.random());
    }


    /**
     * [ ] 使用Math.min(random(),random())可将产生随机数的概率控制为f(x)=1-(1-x)^2：求最小值时要满足在[0,x)范围内，
     * 只要其中一个random()落到[0,x)范围内则满足要求，反过来不满足的的概率为1-x，所以要求满足落到[0,x)范围内的概率，
     * 只需要求两个都不满足落在[0,x)范围内的概率(1-x)^2，剩余的就是满足取min的概率f(x)=1-(1-x)^2
     * @return
     */
    public static double powReduceX() {
        return Math.min(Math.random(),Math.random());
    }


    /**
     * 给定函数 f  函数随机等概率返回 1到5
     * 在f函数基础上 返回一个等概率输出 1到7的函数g
     *
     * 以上可以抽象成：
     * 1、f函数目标函数  都可以转换成01 等概率发生器
     * 2、g函数可能都不是从0 起始的 可能是从 x开始（x>0）  如 (17-56)
     *    可以等价处理成  （0-39）的函数+17
     * 3、然后看看需要几个2进制位
     */
    @Test
    public void test3(){
        //验证概率是否正确
        double testTimes = 10000000;
        int count = 0;
        for (int i = 0; i < testTimes; i++) {
            if(0==f2()){
                count++;
            }
        }
        System.out.println((double) count/testTimes);

        System.out.println("=============验证0-6函数==============");


        //验证0-6函数
        int[] arr = new int[7];
        for (int i = 0; i < testTimes; i++) {
            arr[f4()]++;
        }

        for (int i = 0; i < arr.length; i++) {
            System.out.println(i+"这个数出现了 "+arr[i]+"次");
        }


    }

    //函数随机等概率返回 1到5
    public int f(){
        return (int)(Math.random()*5)+1;
    }

    //随机等概率返回 01 只能用f函数
    public int f2(){
        int ans = 0;
        do {
           ans = f();
        }while (ans==3);
        return ans<3?0:1;
    }

    //随机等概率返回 0-7 范围的函数 利用2进制
    public int f3(){
        return (f2()<<2)+(f2()<<1)+f2();
    }

    //0-6
    //根据f3可以得到
    //只要f3中遇到7 就重新执行
    public int f4(){
        int ans = 0;
        while ((ans=f3())==7){
            ans = f3();
        }
        return ans;
    }

    //最终函数 1到7
    public int g(){
        return f4()+1;
    }


    /**
     * 根据一个输出不等概率的0和1 函数f
     * 获得到一个 输出等概率函数的01 的函数g
     */
    public int fP(){//
        return Math.random()>0.64?1:0;
    }

    public int gp(){
        int ans = 0;
        do{
            ans=fP();
        }while (ans==fP());
        // 01  10 只返回 因为这个两个概率是一样的
        return ans;
    }





}
